TerraformでCycleエラーが起きてるリソースだけを画像で表示して修正する
最近、Terraformが好きだ。 もこ@札幌オフィスです。
aws_security_groupのみを利用してSecurity Groupを構築をしていたら、plan時に20個近くのリソースがCycleエラーになってしまいとても消耗してしまったので、これ以上被害者が増えないようにと本エントリーを投下します。
そもそもCycle エラーとは
リソースの相互参照によってエラーが発生します。
例えば下記のようなコードを実行してみます。
provider "aws" { region = "ap-northeast-1" } resource "aws_security_group" "example-A" { ingress { from_port = 22 protocol = "tcp" to_port = 22 security_groups = [ aws_security_group.example-C.id ] } } resource "aws_security_group" "example-B" { ingress { from_port = 22 protocol = "tcp" to_port = 22 security_groups = [ aws_security_group.example-A.id ] } } resource "aws_security_group" "example-C" { ingress { from_port = 22 protocol = "tcp" to_port = 22 security_groups = [ aws_security_group.example-B.id ] } }
実行結果
Error: Cycle: aws_security_group.example-B, aws_security_group.example-C, aws_security_group.example-A
上記コードですと、 example-B
が example-A
を参照、 example-C
が example-B
を参照するまでは正常ですが、
example-A
が example-C
を参照してしまっており、間接的とはなっていますが相互参照となってしまっています。
修正方法
上記のような場合ですと、Example-AのSecurity Groupを少し修正し、 aws_security_group_rule
を利用してSecurity Groupを指定することによってエラー解決が可能です。
resource "aws_security_group" "example-A" { } resource "aws_security_group_rule" "example-A-rule" { type = "ingress" from_port = 22 protocol = "tcp" to_port = 22 source_security_group_id = aws_security_group.example-C.id security_group_id = aws_security_group.example-A.id }
エラーが起きてるリソースを普通にグラフで出力する
Terraformのコマンドでgraphを出力することも出来ます。
実行にはdotコマンドが必要となっていますので、インストールします。
brew install graphviz
もしくは
pip install graphviz
インストール後、下記コマンドでCycleエラーが起こっているリソースをハイライトしたグラフ画像を出力することが出来ます。
terraform graph -draw-cycles | dot -Tsvg > graph.svg
出力した結果はこんな感じ
薄っすらと左側に赤くなっているのがSecurity Group部分で、Cycleエラーが起こっている部分となっています。(あえて画像を小さくしています)
この画像から追うのはしんどい、エラーが起きてる箇所だけを表示してみる
上記画像とにらめっこしていたら、隣の席のCloudFormationマンに「TerraformしんどそうwCloudFormationだと勝手にいい感じにしてくれるよw」と煽られてしまいましたが、今からCloudFormationで書き直すのはしんどかったので、ひとまずエラーが起こっている部分だけを抽出してみます。
terraform graph -draw-cycles
で出力される形は下記のようになっています。
(例)
digraph { compound = "true" newrank = "true" subgraph "root" { "[root] aws_security_group.hogehoge" [label = "aws_security_group.hogehoge", shape = "box"] "[root] aws_security_group.hugahuga" -> "[root] aws_security_group.hugahuga" [color = "red", penwidth = "2.0"] ...省略 } }
Cycleエラーになっているリソースに対しては [color = "red", penwidth = "2.0"]
が付与されていることがなんとなくわかるかと思います。
grepでCycleエラーが起きているリソースだけを取得してみます。 適当に"red"の部分だけでgrepしてみましょう。
$ terraform graph -draw-cycles | grep "red" "[root] aws_security_group.hogehoge" -> "[root] aws_security_group.hogehoge1" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge1" -> "[root] aws_security_group.hogehoge2" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge2" -> "[root] aws_security_group.hogehoge3" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge3" -> "[root] aws_security_group.hogehoge4" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge4" -> "[root] aws_security_group.hogehoge5" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge5" -> "[root] aws_security_group.hogehoge6" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge6" -> "[root] aws_security_group.hogehoge7" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge7" -> "[root] aws_security_group.hogehoge8" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge8" -> "[root] aws_security_group.hogehoge9" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge9" -> "[root] aws_security_group.hogehoge10" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge10" -> "[root] aws_security_group.hogehoge11" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge11" -> "[root] aws_security_group.hogehoge12" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge12" -> "[root] aws_security_group.hogehoge13" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge13" -> "[root] aws_security_group.hogehoge14" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge14" -> "[root] aws_security_group.hogehoge15" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge15" -> "[root] aws_security_group.hogehoge16" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge16" -> "[root] aws_security_group.hogehoge" [color = "red", penwidth = "2.0"]
これでエラーが起きているリソースだけをGrep出来ましたので、graphvizで表示できる形にしていきます。
subgraph "root" {
の中にgrepした結果を入れていきます
digraph { compound = "true" newrank = "true" subgraph "root" { "[root] aws_security_group.hogehoge" -> "[root] aws_security_group.hogehoge" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge1" -> "[root] aws_security_group.hogehoge1" [color = "red", penwidth = "2.0"] "[root] aws_security_group.hogehoge2" -> "[root] aws_security_group.hogehoge2" [color = "red", penwidth = "2.0"] ...略 } }
適当なファイルで保存して、早速グラフにして開いてみましょう。
cat grepgraph | dot -Tpng > graph.png
これでとても見やすくなりました。めでたしめでたし。
grepでゴリ押したりしていましたが、Goでワンライナーでリソースを絞り込めるスクリプトが公開されました!是非こちらも試してみてください(2020/03/19追記)
まとめ
aws_security_group_rule
を利用して相互参照を解消することによって Cycle Errorを修正することが可能!
terraform graph
コマンドを利用するとTerraformのリソース一覧をグラフ(画像)にして表示することが出来る!
Cycle エラーには [color = "red", penwidth = "2.0"]
が含まれているので、Grepして無駄なものを省いてあげればCycleエラーになっているリソースだけを可視化することが可能!
誰かのお役に立てれば幸いです。